class_name Player
extends UnitBase

@export_category("Move")
@export var friction: float = 700
@export var jump_speed: float = 180
@export var normal_jump_speed: float = 0
@export var view_root_gravity: float = 200
var view_root_velocity: float = 0

@onready var undamagable_animation_player: AnimationPlayer = $UndamagableAnimationPlayer
@onready var body_animation_player: AnimationPlayer = $BodyAnimationPlayer
@onready var hand_animation_player: AnimationPlayer = $HandAnimationPlayer
@onready var hand_hold_parent: Node2D = $ViewRoot/HandHoldParent
@onready var hand_hold_points: Node2D = $ViewRoot/HandHoldPoints
@onready var hand_sprite: Sprite2D = $ViewRoot/Hand
@onready var mouse: Marker2D = $ViewRoot/Mouse
@onready var head_up: Marker2D = $ViewRoot/HeadUp

# off hand
@onready var offhand_parent: Node2D = $ViewRoot/OffhandParent

@export_category("CurrentItem")
var current_hold_item_view: Node2D
var current_offhand_hold_item_view: Node2D

@onready var item_use_cooldown_timer: Timer = $Timers/ItemUseCooldownTimer

# gun hand
@onready var gun_hand: Marker2D = $ViewRoot/GunHand
@onready var gun_hand_hold_parent: Marker2D = $ViewRoot/GunHand/HoldPoint

# pickup
@onready var pickup_area: Area2D = $ViewRoot/PickupArea

@export_category("AttackCharge")
@export var charge_max_time: float = 3.0
@export var charge_timer: float = 0
@export var charge_delay_time: float = 1.0
@onready var particle_charge_point: Marker2D = $ViewRoot/ParticleChargePoint
@onready var particle_charge_release_point: Marker2D = $ViewRoot/ParticleChargeReleasePoint
var current_attack_charge_particle_effect: Node2D = null
@onready var attack_charge_delay_timer: Timer = $Timers/AttackChargeDelayTimer
@export var charge_move_speed: float = 50
@export var charge_anim_scale: float = 0.5

# player data
var player_data: PlayerData = null

# particles
@onready var particles_parent: Node2D = $ParticleParent

# approach target
@onready var approach_target: Marker2D = $ViewRoot/ApproachTarget

# coyote jump
@export var coyote_time: float = 0.1
var coyote_timer: float = 0

# jump buffer
@export var jump_buffer_time: float = 0.1
var jump_buffer_timer: float = 0

# variable jump height
@export var low_jump_multiplier: float = 2.0
@export var fall_multiplier: float = 1.5

# equip
@onready var equip_parent: Marker2D = $ViewRoot/EquipParent
var current_equipment_view: Node2D = null

# can operate
var enable_input: bool = true

# push partner
var is_pushing_partner: bool = false
var pushing_speed_scale: float = 0.3


func init_by_data(data: UnitBaseData):
	player_data = data as PlayerData
	unit_data = player_data
	
	global_position = player_data.pos 
	set_face(player_data.face)
	
	if data.hold_item_data:
		switch_item(data.hold_item_data)
		
	if player_data.equip_item:
		create_equipment_view()
		
	remember_init_speed_gravity()
	normal_jump_speed = jump_speed


func _ready() -> void:
	remember_init_speed_gravity()
	hold_hand()
	GlobalSignals.signal_switch_hold_item.connect(handler_switch_hold_item)
	states.transition(init_state)


func handler_switch_hold_item(item_data: ItemData):
	if item_data.item_config.name == "脱下装备":
		takeoff_equipment()
		return
	
	switch_item(item_data)


func process_move_statemove(delta: float):
	var horizontal = Input.get_axis("move_left", "move_right")
	
	if not enable_input:
		horizontal = 0
	
	if horizontal:
		velocity.x = horizontal * speed
		if is_pushing_partner:
			velocity.x *= pushing_speed_scale
		set_face(horizontal)
	else:
		velocity.x = move_toward(velocity.x, 0, friction * delta)
		

func process_push_partner(delta: float):
	# 用 move_and_collide 测试未来位移，不真的移动
	var collision = move_and_collide(velocity * delta, true)

	if collision:
		var other = collision.get_collider()
		if other is CharacterBody2D and other is UnitBase:
			var unit = other as UnitBase
			if not unit.unit_data.is_enemy:
				is_pushing_partner = true
				
				unit.velocity = velocity.normalized() * speed * pushing_speed_scale
				unit.move_and_slide()
	else:
		is_pushing_partner = false
		

func process_move_attack_charge(delta: float):
	var horizontal = Input.get_axis("move_left", "move_right")
	
	if horizontal:
		velocity.x = horizontal * charge_move_speed
		set_face(horizontal)
	else:
		velocity.x = move_toward(velocity.x, 0, friction * delta)
		

func process_animation_statemove():
	body_animation_player.speed_scale = 1.0
	
	if not is_on_floor():
		body_animation_player.play("jump")
	else:
		if velocity.x:
			body_animation_player.play("walk")
		else:
			body_animation_player.play("idle")
			

func process_animation_attack_charge():
	body_animation_player.speed_scale = charge_anim_scale
	
	if not is_on_floor():
		body_animation_player.play("jump")
	else:
		if velocity.x:
			body_animation_player.play("walk")
		else:
			body_animation_player.play("idle")
			

func set_face(face: int):
	view_root.scale.x = face
	
	
func get_face():
	return view_root.scale.x


func process_gravity(delta: float):
	if not is_on_floor():
		velocity.y += gravity * delta
		
		if velocity.y < 0 and not Input.is_action_pressed("jump"):
			velocity.y += gravity * (low_jump_multiplier - 1) * delta
		
		if velocity.y > 0:
			velocity.y += gravity * (fall_multiplier - 1) * delta
		

func process_vertical_move_viewroot(delta: float):
	view_root_velocity += view_root_gravity * delta
	view_root.position.y += view_root_velocity * delta
	
	if view_root.position.y >= 0:
		view_root.position.y = 0
		view_root_velocity = 0
		

func process_quit_attackdash(delta: float):
	pass
		
		
func set_velocity_y_viewroot(value: float):
	view_root_velocity = value
		

func set_velocity_y(value: float):
	velocity.y = value
		
	
func process_enter_jump():
	if  jump_buffer_timer > 0.0 and (is_on_floor() or coyote_timer > 0.0):
		velocity.y = -jump_speed
		coyote_timer = 0
		jump_buffer_timer = 0
		
		#AudioManager.play_sound("jump")
	

func switch_item(item_data: ItemData):
	clear_current_hold_item()
	clear_current_offhand_hold_item_view()
	
	player_data.hold_item_data = item_data
	GlobalSignals.player_info_changed.emit()
	
	# main hand
	var target_hold_parent = null
	if player_data.hold_item_data.item_config.hand_hold_type == ItemBaseConfig.HandHoldType.Normal:
		hand_sprite.visible = true
		gun_hand.visible = false
		target_hold_parent = hand_hold_parent
	elif player_data.hold_item_data.item_config.hand_hold_type == ItemBaseConfig.HandHoldType.GunHold:
		hand_sprite.visible = false
		gun_hand.visible = true
		target_hold_parent = gun_hand_hold_parent
	
	var resource_name = player_data.hold_item_data.item_config.hold_item_resource_name
	var packed_scene = ResourceManager.get_resource(resource_name) as PackedScene
	var inst = packed_scene.instantiate()
	inst.item_data = item_data
	current_hold_item_view = inst
	inst.holder = self
	target_hold_parent.add_child(inst)
	
	if "bullet_type" not in item_data.item_config:
		level.ui_bullet_remain_info.visible = false
	
	# off hand
	var off_hand_resource_name = player_data.hold_item_data.item_config.off_hand_hold_item_resource_name
	if off_hand_resource_name:
		packed_scene = ResourceManager.get_resource(off_hand_resource_name) as PackedScene
		inst = packed_scene.instantiate()
		offhand_parent.add_child(inst)
		current_offhand_hold_item_view = inst
		inst.holder = self
	
	clear_item_cooldown_timer()
	hold_hand()
	
	
func process_use_item_consumable():
	hand_animation_player.play("use_consumable")
	

func rotate_consumable(angle: float):
	current_hold_item_view.rotate(deg_to_rad(angle))
	
	
func rotate_hold_item_view(angle: float):
	current_hold_item_view.rotate(deg_to_rad(angle))	
	

func use_consumable():
	if not player_data.hold_item_data: return
	if not player_data.hold_item_data.item_config is ConsumableConfig: return
	
	var consumable_config = player_data.hold_item_data.item_config as ConsumableConfig
	if consumable_config.consumable_effect_type == ConsumableConfig.ConsumableEffectType.Heal:
		use_consumable_heal()
	else:
		use_consumable_common()
	
	if consumable_config.consumable_category == ConsumableConfig.ConsumableCategory.Food:
		level.emit_particle_face("particle_crumbs", mouse.global_position, get_face())
		AudioManager.play_sound("sfx_bite_apple")
	if consumable_config.consumable_category == ConsumableConfig.ConsumableCategory.Potion:
		level.emit_particle_face("particle_drink_potion_effect", mouse.global_position, get_face())
		AudioManager.play_sound("sfx_drink")
		

func use_consumable_common():
	current_hold_item_view.use()
	GlobalData.reduce_item(player_data.hold_item_data)
	if player_data.hold_item_data.item_count <= 0:
		clear_current_hold_item()		
		

func use_consumable_heal():
	GlobalData.reduce_item(player_data.hold_item_data)
	
	var config = player_data.hold_item_data.item_config as ConsumableConfig
	player_data.add_hp(config.heal_point)
	GlobalSignals.player_info_changed.emit()
	
	if player_data.hold_item_data.item_count <= 0:
		clear_current_hold_item()


func clear_current_hold_item():
	if current_hold_item_view:
		current_hold_item_view.queue_free()
		current_hold_item_view = null
	
	if player_data.hold_item_data:
		player_data.hold_item_data = null
		
		
func clear_current_offhand_hold_item_view():
	if current_offhand_hold_item_view:
		current_offhand_hold_item_view.queue_free()
		current_offhand_hold_item_view = null
	

func process_use_item_melee_weapon():
	if not item_use_cooldown_timer.is_stopped(): return
	
	if Input.is_action_just_pressed("use"):
		states.transition("AttackCharge")
		

func process_enter_attack_dash():
	if Input.is_action_just_released("use"):
		states.transition("AttackDash")
		

func start_item_cooldown_timer(wait_time: float):
	item_use_cooldown_timer.stop()
	item_use_cooldown_timer.wait_time = wait_time
	item_use_cooldown_timer.start()
	

func clear_item_cooldown_timer():
	item_use_cooldown_timer.stop()
		

func process_use_item():
	if not enable_input: return
	if not player_data.hold_item_data: return
	if not item_use_cooldown_timer.is_stopped() or not Input.is_action_just_pressed("use"): return
	if level.current_quick_switch_item_ui: return
	
	if player_data.hold_item_data.item_config is MeleeWeaponConfig:
		process_use_item_melee_weapon()
	elif player_data.hold_item_data.item_config is ConsumableConfig:
		process_use_item_consumable()
	elif player_data.hold_item_data.item_config is ItemSpellSummonConfig:
		process_use_item_spell_summon()
	elif player_data.hold_item_data.item_config is ItemEquipmentConfig:
		process_use_item_equipment()
	elif player_data.hold_item_data.item_config is ItemGunBaseConfig:
		process_use_item_gun()
	elif player_data.hold_item_data.item_config is ItemThrowWeaponConfig:
		process_use_item_throw_weapon()
	elif player_data.hold_item_data.item_config is ItemBowConfig:
		process_use_item_bow()
	elif player_data.hold_item_data.item_config is ItemLightBagConfig:
		process_use_item_light_bag()
	elif player_data.hold_item_data.item_config is MagicStaffConfig:
		process_use_item_magic()
	elif player_data.hold_item_data.item_config is ItemRandomTransferScrollConfig:
		process_use_item_random_transfer_scroll()
	elif player_data.hold_item_data.item_config is ItemBaseConfig:
		process_use_item_base()
	
	if player_data.hold_item_data:
		start_item_cooldown_timer(player_data.hold_item_data.item_config.item_use_cooldown_time)	


func process_use_item_throw_weapon():
	if current_hold_item_view.has_method("use"):
		current_hold_item_view.use()


func process_use_item_random_transfer_scroll():
	if current_hold_item_view.has_method("use"):
		current_hold_item_view.use()


func process_use_item_magic():
	var config = player_data.hold_item_data.item_config as MagicStaffConfig
	raise_hand()
	await get_tree().create_timer(0.3).timeout
	if current_hold_item_view.has_method("use"):
		current_hold_item_view.use()
	await get_tree().create_timer(config.use_time).timeout
	hold_hand()
	

func process_use_item_base():
	if current_hold_item_view.has_method("use"):
		current_hold_item_view.use()


func process_use_item_light_bag():
	current_hold_item_view.use()		
		
		
func process_use_item_gun():
	current_hold_item_view.shoot()
	

func process_use_item_bow():
	current_hold_item_view.shoot()


func process_use_item_equipment():
	hand_animation_player.play("use_equipment")
	

func use_equipment():
	takeoff_equipment()
	
	player_data.equip_item = player_data.hold_item_data
	player_data.items.erase(player_data.equip_item)
	
	create_equipment_view()
	
	clear_current_hold_item()
	

func create_equipment_view():
	var equipment_config = player_data.equip_item.item_config as ItemEquipmentConfig
	var equipment_view = (ResourceManager.get_resource(equipment_config.equip_resource_name) as PackedScene).instantiate()
	equipment_view.item_data = player_data.equip_item
	equipment_view.holder = self
	equip_parent.add_child(equipment_view)
	current_equipment_view = equipment_view
	
	if equipment_config.equip_back:
		equip_parent.z_index = -1
	else:
		equip_parent.z_index = 0
	

func fade_transparent():
	var tween_alpha = create_tween()
	tween_alpha.tween_property(view_root, "modulate:a", 0.3, 0.5) \
	.set_trans(Tween.TRANS_SINE) \
	.set_ease(Tween.EASE_IN_OUT).from_current()


func recover_fade_transparent():
	var tween_alpha = create_tween()
	tween_alpha.tween_property(view_root, "modulate:a", 1.0, 0.5) \
	.set_trans(Tween.TRANS_SINE) \
	.set_ease(Tween.EASE_IN_OUT).from_current()
	

func takeoff_equipment():
	if player_data.equip_item:
		player_data.items.append(player_data.equip_item)
		player_data.equip_item = null
		
		if current_equipment_view:
			current_equipment_view.queue_free()
			current_equipment_view = null


func process_use_item_spell_summon():
	var config = player_data.hold_item_data.item_config as ItemSpellSummonConfig
	var units_exists_by_this_summon_spell = level.find_summon_units_by_summon_item_name(config.name)
	if units_exists_by_this_summon_spell.size() >= config.max_summon_count:
		return
	
	var bullet_resource_name = config.bullet_resource_name
	var bullet_inst = (ResourceManager.get_resource(bullet_resource_name) as PackedScene).instantiate() as SpellSummonBullet
	level.bullets_parent.add_child(bullet_inst)
	bullet_inst.level = level
	
	var dir = GlobalUtils.get_rand_dir_between_angle(config.shoot_start_angle, config.shoot_end_angle)
	dir *= get_face()
	
	if GlobalData.find_item_by_name("眼镜"):
		dir = (level.cross_hair.global_position - hand_hold_parent.global_position).normalized()
	
	bullet_inst.set_is_enemy(false)
	bullet_inst.global_position = hand_hold_parent.global_position
	bullet_inst.set_start_velocity(dir * config.shoot_speed)
	

func melee_weapon_attack():
	await get_tree().process_frame
	current_hold_item_view.attack()
	

func raise_hand():
	hand_hold_parent.position = hand_hold_points.find_child("Raise").position
	hand_sprite.frame = 1
		
	
func hold_hand():
	hand_hold_parent.position = hand_hold_points.find_child("Hold").position
	hand_sprite.frame = 0
	

func is_in_hold_hand():
	return hand_sprite.frame == 0
	
	
func attack_hand():
	hand_hold_parent.position = hand_hold_points.find_child("Attack").position
	hand_sprite.frame = 2
		
		
func process_pickup():
	var drop_items = pickup_area.get_overlapping_bodies()
	if drop_items.size() <= 0: return
	
	for drop_item: DropItem in drop_items:
		var item_config = drop_item.item_config
		
		if item_config is ItemGoldConfig:
			process_pickup_gold(drop_item)
		elif item_config is ItemExpConfig:
			process_pickup_exp(drop_item)
		elif item_config is ItemKeyConfig:
			process_pickup_key(drop_item)
			level.show_pickup_tip(item_config, head_up.global_position)
		else:
			GlobalData.add_item(item_config)
			level.show_pickup_tip(item_config, head_up.global_position)
			
		drop_item.on_pickup()
		AudioManager.play_sound("sfx_pickup")


func process_pickup_key(drop_item: DropItem):
	var config = drop_item.item_config as ItemKeyConfig
	if config.is_big_key:
		player_data.big_key_num += 1
	else:
		player_data.little_key_num += 1
	GlobalSignals.player_info_changed.emit()


func process_pickup_gold(drop_item: DropItem):
	player_data.gold += 1
	GlobalSignals.player_info_changed.emit()
	

func process_pickup_exp(drop_item: DropItem):
	var old_level = player_data.level
	player_data.add_exp(1)
	var new_level = player_data.level
	GlobalSignals.player_info_changed.emit()
	
	if new_level != old_level:
		var particle_inst = (ResourceManager.get_resource("particle_level_up") as PackedScene).instantiate()
		particles_parent.add_child(particle_inst)
		particle_inst.position = Vector2(0, 6)
		
		#AudioManager.play_sound("sfx_level_up")
	
		
func process_quick_switch_item():
	if not enable_input: return
	
	if Input.is_action_just_pressed("quick_select_item_type_consumable") and not is_using_item():
		level.open_quick_switch_item_by_type([ItemBaseConfig.ItemType.Consumable, ItemBaseConfig.ItemType.Light, ItemBaseConfig.ItemType.LightBag, ItemBaseConfig.ItemType.RandomTransferScroll, ItemBaseConfig.ItemType.Item])
	if Input.is_action_just_pressed("quick_select_item_type_melee_weapon") and not is_using_item():
		level.open_quick_switch_item_by_type([ItemBaseConfig.ItemType.MeleeWeapon])
	if Input.is_action_just_pressed("quick_select_item_type_spell") and not is_using_item():
		level.open_quick_switch_item_by_type([ItemBaseConfig.ItemType.Spell, ItemBaseConfig.ItemType.Magic])
	if Input.is_action_just_pressed("quick_select_item_type_equipment") and not is_using_item():
		level.open_quick_switch_item_by_type([ItemBaseConfig.ItemType.Equipment])
	if Input.is_action_just_pressed("quick_select_item_type_gun") and not is_using_item():
		level.open_quick_switch_item_by_type([ItemBaseConfig.ItemType.Gun])
		
	if Input.is_action_just_released("quick_select_item_type_consumable") \
		or Input.is_action_just_released("quick_select_item_type_melee_weapon") \
		or Input.is_action_just_released("quick_select_item_type_spell") \
		or Input.is_action_just_released("quick_select_item_type_equipment") \
		or Input.is_action_just_released("quick_select_item_type_gun"):
		level.destroy_current_quick_switch_item_ui()
	

func is_using_item():
	return hand_animation_player.is_playing() or not is_in_hold_hand()
		

func get_attack_charge_rate():
	var rate = charge_timer / charge_max_time
	rate = clamp(rate, 0, 1.0)
	return rate
	
		
func process_attack_charge(delta: float):
	if not current_attack_charge_particle_effect: return
	
	charge_timer += delta
	
	var rate = get_attack_charge_rate()
	
	current_attack_charge_particle_effect.set_power(rate)
	

func create_charge_particle_effect():
	destroy_current_attack_charge_particle_effect()
	
	var inst = ResourceManager.get_resource("particle_attack_charged_effect").instantiate()
	particle_charge_point.add_child(inst)
	current_attack_charge_particle_effect = inst
	current_attack_charge_particle_effect.set_power(0)
	

func destroy_current_attack_charge_particle_effect():
	if current_attack_charge_particle_effect: 
		current_attack_charge_particle_effect.stop_and_destroy_delay(2)
		current_attack_charge_particle_effect = null
		
		
func create_charge_release_particle_effect():
	var inst = ResourceManager.get_resource("particle_charged_attack_release_effect").instantiate()
	get_tree().current_scene.add_child(inst)
	inst.global_position = particle_charge_release_point.global_position
	inst.set_face(get_face())
	inst.play()
		
		
func collect_data():
	player_data.pos = global_position
	player_data.face = get_face()
	
	
func take_hit(hit_data: HitData, source: Node2D = null):
	if is_dead: return
	
	# frog hat equip
	if source is UnitBase and source.is_enemy and player_data.equip_item and player_data.equip_item.item_config.name == "毒蛙帽":
		current_equipment_view.add_poison_debuff(source)
	
	# ice sword equip
	if source is UnitBase and source.is_enemy and player_data.equip_item and player_data.equip_item.item_config.name == "冰霜之剑":
		current_equipment_view.add_frozen_debuff(source)
	
	# balance scale
	if source is UnitBase and source.is_enemy and player_data.equip_item and player_data.equip_item.item_config.name == "善恶天平":
		var is_heal = randf() < 0.5
		if is_heal:
			hit_data.is_heal = true
			if hit_data.is_direct_damage:
				hit_data.heal_point = hit_data.direct_damage
			else:
				hit_data.heal_point = hit_data.attack
			level.emit_particle("particle_add_life_effect", ray_cast_point.global_position)
		else:
			if hit_data.is_direct_damage:
				hit_data.direct_damage *= 2.0
			else:
				hit_data.attack *= 2.0
	
	# bat hat
	if source is UnitBase and source.is_enemy and player_data.equip_item and player_data.equip_item.item_config.name == "蝙蝠帽":
		current_equipment_view.born_unit()
	
	# return attack
	if source is UnitBase and source.is_enemy and player_data.equip_item and player_data.equip_item.item_config.name == "海胆衣":
		var return_hit_data = HitData.new()
		return_hit_data.is_critical = true
		return_hit_data.is_direct_damage = true
		return_hit_data.direct_damage = hit_data.attack * player_data.equip_item.item_config.return_attack_rate
		return_hit_data.knockback = player_data.equip_item.item_config.return_knockback
		
		source.take_hit(return_hit_data, self)
	
	var buff_inst = get_buff_by_name("buff_undamagable")
	if buff_inst:
		return
	
	current_hit_data = hit_data
	
	if hit_data.is_heal:
		player_data.add_hp(hit_data.heal_point) 
		current_hit_data.actual_damage = hit_data.heal_point
	elif hit_data.direct_dead:
		current_hit_data.actual_damage = unit_data.hp
		unit_data.hp = 0
	elif hit_data.is_direct_damage:
		current_hit_data.actual_damage = hit_data.direct_damage
		unit_data.hp -= hit_data.direct_damage
	else:
		var damage = get_damage(current_hit_data.attack)
		player_data.hp -= damage
		current_hit_data.actual_damage = damage
	
	if player_data.hp <= 0:
		player_data.hp = 0
		
		# test reborn
		var item_data = GlobalData.find_item_by_name("复生十字")
		if item_data:
			GlobalData.reduce_item(item_data)
			if item_data.item_count <= 0 and player_data.hold_item_data and player_data.hold_item_data.item_config.name == item_data.item_config.name:
				clear_current_hold_item()
				
			player_data.hp = player_data.max_hp
			var particle_inst = (ResourceManager.get_resource("particle_reborn") as PackedScene).instantiate()
			particles_parent.add_child(particle_inst)
		else:
			is_dead = true
	
	GlobalSignals.player_info_changed.emit()
	states.transition("Hit")
	
	close_ui_card()
	if level.ui_game_pause_menu.is_open():
		level.hide_game_pause_menu()
	
	AudioManager.play_sound("sfx_hit")
	
	
func get_random_hurt_position():
	var shape = hurt_collision_shape.shape as RectangleShape2D
	var rand_x_offset = randf_range(-shape.size.x / 2.0, shape.size.x / 2.0)
	var rand_y_offset = randf_range(-shape.size.y / 2.0, shape.size.y / 2.0)
	var offset = Vector2(rand_x_offset, rand_y_offset)
	
	var target_position = hurt_collision_shape.global_position + offset
	return target_position
	

func get_damage(attack: int):
	var result = attack - player_data.get_defense()
	if result <= 1: result = 1
	
	return result


func pause_animation():
	body_animation_player.pause()
	hand_animation_player.pause()
	
	
func death_fade():
	var tween_alpha = create_tween()
	tween_alpha.tween_property(self, "modulate:a", 0, 0.5) \
	.set_trans(Tween.TRANS_SINE) \
	.set_ease(Tween.EASE_OUT)
	
	await tween_alpha.finished
	queue_free()


func process_drop_item():
	if not enable_input: return
	if not Input.is_action_just_pressed("drop_item"): return
	if not player_data.hold_item_data: return
	if not player_data.hold_item_data.item_config.can_drop: return
	
	AudioManager.play_sound("sfx_throw_item")
	level.throw_item(player_data.hold_item_data.item_config.drop_item_name, global_position + Vector2.RIGHT * 16 * get_face() + Vector2.UP * 16, get_face())
	
	GlobalData.reduce_item(player_data.hold_item_data)
	if player_data.hold_item_data.item_count <= 0:
		clear_current_hold_item()
	

func drop_current_hold_item_no_fly(target_position: Vector2):
	level.drop_item_no_fly(player_data.hold_item_data.item_config.drop_item_name, target_position)
	
	GlobalData.reduce_item(player_data.hold_item_data)
	if player_data.hold_item_data.item_count <= 0:
		clear_current_hold_item()
	

func disable_body_collision():
	body_collision_shape_2d.disabled = true
	

func process_exit_stuck(delta):
	#var horizontal = Input.get_axis("move_left", "move_right")
	#if horizontal and velocity.length() <= 1 and not is_on_wall():
		#var nudge = Vector2(randf_range(-10, 10), randf_range(-10, 10))
		#move_and_collide(nudge * delta)
	pass
		
		
func process_gun_hand_rotation():
	if not enable_input: return
	
	if not gun_hand.visible: return
	
	var mouse_world_position = level.screen_2_world_pos(get_viewport().get_mouse_position()) 
	var dir = (mouse_world_position - gun_hand.global_position).normalized()
	
	var angle = dir.angle()
	if get_face() == 1:  # 面向右
		var min_angle = deg_to_rad(-90)
		var max_angle = deg_to_rad(90)
		angle = clamp(angle, min_angle, max_angle)
	else: 
		# 镜像角度
		angle = PI - angle
		
		# 归一化到 [-PI, PI]
		angle = wrapf(angle, -PI, PI)
		
		# 限制在 [-90°, 90°]
		angle = clamp(angle, deg_to_rad(-90), deg_to_rad(90))
	
	gun_hand.rotation = angle
		
		
func process_coyote_time(delta: float):
	if is_on_floor():
		coyote_timer = coyote_time
	else:
		coyote_timer -= delta
		

func process_jump_buffer(delta):
	if enable_input and Input.is_action_just_pressed("jump"):
		jump_buffer_timer = jump_buffer_time
	else:
		jump_buffer_timer -= delta
		

func process_open_ui_card():
	return
	
	if level.current_quick_switch_item_ui: return
	
	if Input.is_action_just_pressed("ui_card"):
		if level.ui_card_view:
			close_ui_card()
		else:
			open_ui_card()
	
	if Input.is_action_just_pressed("exit") and level.ui_card_view:
		close_ui_card()


func open_ui_card():
	level.open_ui_card()
	enable_input = false
	Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
	level.hide_crosshair()
	level.camera.is_following = false
	

func close_ui_card():
	level.destroy_ui_card()
	enable_input = true
	Input.mouse_mode = Input.MOUSE_MODE_HIDDEN
	level.show_crosshair()
	level.camera.is_following = true
	

func process_will_collider_with_unit(delta: float):
	var will_collision = move_and_collide(velocity * delta, true)
	if will_collision:
		var collider = will_collision.get_collider()
		if collider is CharacterBody2D and collider.collision_layer & (1 << 7):
			velocity = velocity.slide(will_collision.get_normal())
			
			
func _physics_process(delta: float) -> void:
	process_drop_damage()
	process_speed_affect()
	

func process_speed_affect():
	var target_speed: float = normal_speed
	var target_gravity: float = normal_gravity
	var target_jump_speed: float = normal_jump_speed
	
	# is in water
	var is_in_water: bool = false
	for water_rect in level.all_water_rects:
		if water_rect.has_point(ray_cast_point.global_position):
			is_in_water = true
			break
	
	if is_in_water:
		is_in_water = true
		target_speed = target_speed * water_speed_scale
		target_gravity = target_gravity * water_gravity_scale
	else:
		is_in_water = false
	
	var debuff_numb = get_buff_by_name("debuff_numb")
	if debuff_numb:
		target_speed = target_speed * debuff_numb.numb_speed_scale
		target_jump_speed = target_jump_speed * debuff_numb.numb_speed_scale
		
	if player_data.equip_item:
		var equip_add_speed = player_data.equip_item.item_config.add_speed
		target_speed += equip_add_speed
		
		var equip_add_jump_speed = player_data.equip_item.item_config.add_jump_speed
		target_jump_speed += equip_add_jump_speed 
		
	speed = target_speed
	gravity = target_gravity
	jump_speed = target_jump_speed
	
	
	
	
	
	
	
	
